<template>
  <view class="page">
    <uni-drawer @change="onChangeDrawer" ref="drawerRef">
      <!-- 历史会话区 -->
      <scroll-view @click="hideChatOpitons" class="chatHistory" style="height: 100%;" scroll-y="true">
        <view class="chatHistory-title">历史会话</view>
        <view @click.stop="onClickChatItem(item)" v-for="item in chatHistory.list" :key="item.id" :class="[
          'chatHistoryItem',
          { 'is-active': item.id === chatHistory.questionId },
          { 'is-edit': item._editTitle },
        ]">
          <view v-if="!item._editTitle" class="chatHistoryItem-title">
            {{ item.title }}
          </view>
          <input v-else v-model="item.title" @blur="updateTitle(item)" class="chatHistoryItem-title" type="text"
          :focus="item._editTitle" :disabled="!item._editTitle" />
          <view @click.stop="showHandle(item)" class="chatHistoryItem-handle">
            <uni-icons type="more-filled" color="inherit" size="36rpx"></uni-icons>
          </view>
          <view v-if="item.showOptions" class="chatHistoryItem-options">
            <view @click.stop="onClickRenameTitle(item)" class="chatHistoryItem-option">
              <uni-icons style="margin-right: 12rpx;" type="compose" color="#05073b" size="40rpx"></uni-icons>
              重命名
            </view>
            <view @click.stop="onClickDeleteChat(item)" class="chatHistoryItem-option">
              <uni-icons style="margin-right: 12rpx;" type="trash" color="#05073b" size="40rpx"></uni-icons>
              删除
            </view>
          </view>
          <view class="chatHistoryItem-mask"></view>
        </view>
      </scroll-view>
    </uni-drawer>
    <view class="chat">
      <view class="chat--isEmpty">

      </view>
      <!-- 头部 -->
      <!-- <view class="chat__header">
        <uni-icons type="chatbubble" color="#05073b" size="54rpx"></uni-icons>
        <uni-icons type="list" color="#05073b" size="54rpx"></uni-icons>
      </view> -->

      <!-- 消息区 -->
      <scroll-view class="chat__body" scroll-y :scroll-into-view="data.scrollId" enable-flex>
        <view class="chat-messageItem">
          <image class="chat-messageItem__avatar" src="/static/ai-avatar.png" style="margin-right: 20rpx;"
            mode="aspectFill"></image>
          <view class="chat-messageItem__inner chat__message--received">
            <text class="chat-messageItem__text">你好！有什么可以帮助你的？</text>
          </view>
        </view>
        <view v-for="(item, index) in answerList" :key="item.id"
          :class="['chat-messageItem', { 'is-sent': item.role === 'user' }]">
          <image v-if="item.role === 'assistant'" class="chat-messageItem__avatar" src="/static/ai-avatar.png"
            style="margin-right: 20rpx;" mode="aspectFill"></image>

          <view class="chat-messageItem__inner">
            <view v-if="item.role === 'assistant'" class="chat-messageItem__reasoning">{{ item.reasoning_content }}
            </view>
            <uni-icons v-if="item.role === 'assistant' && !item.content && data.submitLoading" class="uni-icons-loading"
              type="spinner-cycle" color="inherit" size="36rpx"></uni-icons>
            <view class="chat-messageItem__text">{{ item.content }}</view>
            <view v-if="item.role === 'assistant' && !data.submitLoading" class="chat-messageItem__handle">
              <image @click="onCopy(item)" src="/static/icon-copy.png" class="icon-copy"></image>
              <uni-icons @click="reAnswer(index, true)" type="loop" color="inherit" size="36rpx"></uni-icons>
            </view>
          </view>
          <template v-if="item.role === 'user'" class="">
            <!-- <image v-if="student" class="chat-messageItem__avatar" :src="student.avatar" style="margin-left: 20rpx;" mode="aspectFill"></image> -->
            <image class="chat-messageItem__avatar" style="margin-left: 20rpx;" src="/static/ai-question-avatar.png"
              mode="aspectFill"></image>
          </template>
        </view>
        <view id="last-scrollItem" style="padding-bottom: 1px;"></view>
      </scroll-view>
      <!-- 输入区域 -->
      <view class="chat__footer">
        <view class="chat__inputInner">
          <textarea class="chat__inputInner-field" placeholder="输入消息..." v-model="answerKeyword"
            @confirm="sendMessage" />
          <view style="display: flex; justify-content: space-between; padding: 0 12rpx 12rpx 12rpx;">
            <view class="action">
              <view @click="$refs.drawerRef.open()" class="actionItem">
                <uni-icons type="list" color="#05073b" size="40rpx"></uni-icons>
              </view>
              <view @click="onClickCreateChat" class="actionItem">
                <uni-icons type="chatbubble" color="#05073b" size="40rpx"></uni-icons>
                新会话
              </view>
            </view>
            <view class="">
              <button v-if="!data.submitLoading" class="chat__send-button" @click="sendMessage">
                <uni-icons type="arrow-up" color="inherit" size="40rpx"></uni-icons>
              </button>
              <button v-else class="chat__send-button" @click="onStopStream">
                <view class="icon-stop" style="font-size: 40rpx;"></view>
              </button>
            </view>
          </view>
        </view>
      </view>
    </view>
  </view>
</template>

<script setup>
import { ref, toRefs, reactive, nextTick } from "vue";


let drawerRef = ref()
let data = reactive({
  scrollId: '', // 滚动条位置
  buffer: '',   // 用于存储流式数据不完整的行
  defaultQuestion: {
    list: [
      { id: 1, value: '西安交通大学怎么样？', },
      { id: 2, value: '统计学专业就业好不好？', },
      { id: 3, value: '产品经理职业需要具备什么技能？', },
      { id: 4, value: '汉语言与汉语言文学专业有什么区别？', },
    ]
  },
  chatHistory: {
    questionId: undefined,
    list: [],
  },
  answerList: [],
  answerKeyword: '',
  submitLoading: false,
})

let { chatHistory, answerList, answerKeyword } = toRefs(data)


getAIChatList()

function getAIChatList() {
  setTimeout(() => {
    chatHistory.value.list = [
      {
        "id": 31,
        "title": "统计学专业就业好不好？",
        "createDate": "2024-07-31 15:13:49",
        "updateDate": null,
        "itemList": [
          {
            "id": 136,
            "questionId": 30,
            "role": "user",
            "content": "统计学专业就业好不好？",
            "createDate": "2024-07-31 15:13:49",
            "updateDate": null
          },
          {
            "id": 137,
            "questionId": 30,
            "role": "assistant",
            "content": "#### 统计学专业的就业前景相对较好，具有广阔的发展空间。",
            "reasoning_content": "统计学专业的就业前景相对较好，具有广阔的发展空间。",
            "createDate": "2024-07-31 15:13:49",
            "updateDate": null
          }
        ]
      }
    ]
  }, 500)

}

function onCopy(item) {
  uni.setClipboardData({
    data: item.content,
  });
}
function onClickCreateChat() {
  if (chatHistory.value.list.length >= 10) {
    return uni.$toast('您最多可同时建立10个对话，如需新建对话，请先删除会话！')
  }
  chatHistory.value.questionId = undefined
  answerList.value = []
  answerKeyword.value = ''
}

function showHandle(currentItem) {
  // 关闭其他所有选项菜单
  chatHistory.value.list.forEach(item => {
    if (item.id !== currentItem.id) {
      item.showOptions = false
    }
  })
  // 切换当前项的菜单显示状态
  currentItem.showOptions = !currentItem.showOptions
}

function onClickRenameTitle(item) {
  item._editTitle = true
  hideChatOpitons()
}

// 修改标题 请自行填写接口路径
function updateTitle(item) {
  item._editTitle = false
  uni.$http.request({
    url: '',
    method: 'post',
    data: {
      id: item.id,
      title: item.title,
    },
  }).then(res => {
    uni.$toast('修改成功')
    getAIChatList()
  })
}

function onClickDeleteChat(item) {
  uni.showModal({
    title: '提示',
    content: '确定删除该对话吗？',
    success: (res) => {
      if (res.confirm) {
        deleteChat(item)
      }
    },
  })
}

// 删除对话 请自行填写接口路径
function deleteChat(item) {
  uni.showLoading()
  uni.$http.request({
    url: '/mendao/ai/deleteChat',
    method: 'post',
    data: {
      id: item.id,
    },
  }).then(res => {
    if (item.id === chatHistory.value.questionId) {
      answerList.value = []
    }
    uni.$toast('删除成功')
    getAIChatList()
  })
}

function onClickChatItem(item) {
  if (item._editTitle) {
    return
  }
  chatHistory.value.questionId = item.id
  answerList.value = item.itemList
  hideChatOpitons()
  drawerRef.value.close()
  setScrollbarPosition()
}

function sendMessage() {
  if (answerKeyword.value.trim() == "") {
    return uni.$toast('请输入内容')
  }
  if (answerList.value.length >= 30) {
    return uni.$toast('每个对话窗口最多可提30个问题，超过该限额请新建对话窗口。')
  }
  if (data.submitLoading) return
  data.submitLoading = true
  // 用户提问
  data.answerList.push({
    role: 'user',
    content: answerKeyword.value,
  })
  answerKeyword.value = undefined
  fetchStream(false)
}

let requestTask = null
function fetchStream(reAnswer = false) {
  setScrollbarPosition()
  requestTask = uni.request({
    url: '你的流式接口',
    method: 'POST',
    enableChunked: true,
    responseType: 'text',
    headers: {},
    data: {
      questionId: chatHistory.value.questionId,
      reAnswer: reAnswer,
      list: answerList.value.slice(-30),
    },
    success: res => { 
      console.log('res', res);
      data.submitLoading = false;
      setScrollbarPosition()
    }
  })

  answerList.value.push({
    role: 'assistant',
    content: '',
    reasoning_content: '',
  })
  requestTask.onChunkReceived(chunkFn)
}

function reAnswer(index, boolean) {
  if (data.submitLoading) return
  data.submitLoading = true
  answerList.value = answerList.value.slice(0, index)
  fetchStream(index, boolean)
}

function onStopStream() {
  if (requestTask) {
    requestTask.offChunkReceived(chunkFn)
    requestTask = null
    data.submitLoading = false
  }
}
function chunkFn(res) {
  processSSEChunk(res.data)
}

function processSSEChunk(chunk) {
  const textChunk = decodeUTF8(chunk);
  data.buffer += textChunk;
  const lines = data.buffer.split('data:');
  if (!textChunk.endsWith('\n')) {
    data.buffer = lines.pop();
  } else {
    data.buffer = '';
  }
  lines.forEach((line) => {
    const jsonString = line
    // 结束标识处理
    if (jsonString === '[DONE]') {
      answerList.value[answerList.value.length - 1].content += '';
      data.submitLoading = false;
      requestTask.offChunkReceived(chunkFn)
      requestTask = null
      return;
    } else if (jsonString !== '') {
      try {
        const jsonData = JSON.parse(jsonString);
        if (jsonData.questionId && !chatHistory.value.questionId) {
          this.getAIChatList()
          chatHistory.value.questionId = jsonData.questionId
        }
        if (jsonData.reasoning_content) {
          answerList.value[answerList.value.length - 1].reasoning_content += jsonData.reasoning_content
        } else if (jsonData.content) {
          answerList.value[answerList.value.length - 1].content += jsonData.content
        }
      } catch (e) {
        // console.log('JSON解析错误', e);
      }
    }
    setScrollbarPosition()
  })
}

function decodeUTF8(data) {
  // 将二进制数据转为Uint8数组
  const uint8Array = new Uint8Array(data);
  // 传统方式转换字符串（兼容旧环境）
  let string = '';
  for (let i = 0; i < uint8Array.length; i++) {
    string += String.fromCharCode(uint8Array[i]);
  }
  // 双重解码处理特殊字符（如中文）
  return decodeURIComponent(escape(string));
}

function onChangeDrawer(value) {
  if (!value) {
    hideChatOpitons()
  }
}

function hideChatOpitons() {
  chatHistory.value.list.forEach(item => item.showOptions = false)
}

function setScrollbarPosition() {
  data.scrollId = ''
  nextTick(async () => {
    data.scrollId = 'last-scrollItem'
  });
}
</script>

<style lang="scss" scoped>
$primary-color: #007bff;
$background-color: #e5e5e5;

@mixin setIconSize($size: 30rpx) {
  width: $size;
  height: $size;
  vertical-align: top;
}

@mixin setSafeAreaInsetBottom($padding: 0) {
  padding-bottom: calc(constant(safe-area-inset-bottom) / 2 + $padding);
  padding-bottom: calc(env(safe-area-inset-bottom) / 2 + $padding);
}

// #ifdef H5
uni-page-body, .page {
  height: 100%;
}
// #endif

// #ifdef MP-WEIXIN	
.page {
  height: 100vh;
}
// #endif

.page {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.icon-copy {
  width: 40rpx;
  height: 40rpx;
  vertical-align: top;
}

.icon-stop {
  width: 26rpx;
  height: 26rpx;
  border-radius: 4rpx;
  background: #fff;
}

/* 页面容器 */
.chat {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: linear-gradient(180deg, #f5f4f6, #e6ebf7);
  box-shadow: 0 0 20rpx rgba(0, 0, 0, 0.1);
}

.chat__header {
  color: #fff;
  padding: 10rpx 30rpx;
  /* 30rpx */
  text-align: right;
}

.chat__body {
  flex: 1;
  padding: 20rpx 30rpx 0;
  /* 30rpx */
  overflow-y: auto;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
}

.chat__footer {
  padding: 20rpx;
  /* 20rpx */
  background-color: #fff;
  border-top: 2rpx solid #ddd;
  /* 2rpx */
  @include setSafeAreaInsetBottom(20rpx);
}

.chat-messageItem {
  display: flex;
  font-size: 24rpx;

  &.is-sent {
    justify-content: flex-end;

    .chat-messageItem__inner {
      background-color: #d6e0fb;
      color: #05073b;
    }
  }

  &__avatar {
    @include setIconSize(70rpx);
    flex-shrink: 0;
  }

  &__text {
    word-wrap: break-word;
    word-break: break-all;
    margin: 0;
  }

  &__inner {
    box-shadow: 0 16px 20px 0 rgba(174, 167, 223, .06);
    margin-bottom: 30rpx;
    padding: 20rpx;
    border-radius: 20rpx;
    background-color: #fff;
  }

  &__handle {
    margin-top: 20rpx;
    color: #4f4f5c;
    display: grid;
    gap: 8rpx;
    grid-template-columns: repeat(auto-fill, 40rpx);
  }

  &__reasoning {
    color: #666;
    border-left: 6rpx solid #ddd;
    padding-left: 20rpx;
    margin: 5px 0;
    line-height: 1.5;
  }
}

.chat__inputInner {
  border: 2rpx solid #ddd;
  border-radius: 24rpx;
}

/* 输入框 */
.chat__inputInner-field {
  width: 100%;
  padding: 20rpx;
  box-sizing: border-box;
  height: 110rpx;
  font-size: 28rpx;
}

/* 发送按钮 */
.chat__send-button {
  background-color: #007bff;
  color: #fff;
  border: none;
  border-radius: 10rpx;
  line-height: 1;
  cursor: pointer;
  font-size: 32rpx;
  width: 56rpx;
  height: 56rpx;
  display: inline-flex;
  border-radius: 50%;
  padding: 0;
  align-items: center;
  justify-content: center;
  margin: 0;
  font-weight: 600;
}

.actionItem {
  display: inline-flex;
  align-items: center;
  font-size: 24rpx;
  border: 1rpx solid rgba(0, 0, 0, 0.12);
  border-radius: 40rpx;
  padding: 4rpx 20rpx;

  &:hover {
    background-color: #E0E4ED;
  }

  &+& {
    margin-left: 10rpx;
  }
}

.chatHistory {
  background: #fff;
  padding: 0 20rpx;
  @include setSafeAreaInsetBottom(20rpx);

  &-title {
    z-index: 2;
    color: #555;
    padding: 0 20rpx 0 20rpx;
    font-size: 28rpx;
    font-weight: 600;
    line-height: 38rpx;
    position: -webkit-sticky;
    position: sticky;
    top: 0;
    height: 76rpx;
    line-height: 76rpx;
    background: #fff;
    // border-bottom: .5px solid rgba(0, 0, 0, 0.12);
    margin-bottom: 10rpx;
  }

  &Item {
    height: 76rpx;
    cursor: pointer;
    border-radius: 24rpx;
    align-items: center;
    padding: 0 20rpx;
    font-size: 28rpx;
    display: flex;
    position: relative;

    &.is-active {
      background: rgb(219, 234, 254);
    }

    &.is-edit {
      border: .5px solid #007bff;
      border-radius: 24rpx;
    }

    &-title {
      font-size: 28rpx;
      white-space: nowrap;
      flex: 1;
      align-items: center;
      min-width: 0;
      line-height: 18px;
      display: flex;
      overflow: hidden;
    }

    &-handle {
      z-index: 1;
      border-radius: 8px;
      outline: none;
      justify-content: center;
      align-items: center;
      width: 24px;
      height: 24px;
      display: flex;
      position: absolute;
      right: 20rpx;
    }

    &-mask {
      pointer-events: none;
      border-top-right-radius: 12px;
      border-bottom-right-radius: 12px;
      position: absolute;
      top: 0;
      bottom: 0;
      right: 0;
      width: 104rpx;
      background-image: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, #fff 70%);
      pointer-events: none;
    }

    &-options {
      position: absolute;
      right: 20rpx;
      top: 76rpx;
      background: #fff;
      border-radius: 24rpx;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
      z-index: 10;

      .chatHistoryItem-option {
        padding: 20rpx 30rpx;
        font-size: 28rpx;
        white-space: nowrap;
        cursor: pointer;
        display: flex;
        align-items: center;

        &+& {
          border-top: 1px solid #eee;
        }
      }
    }
  }
}

.uni-icons-loading {
  ::v-deep {
    .uni-icons {
      -webkit-animation: rotating 2s linear infinite;
      animation: rotating 2s linear infinite;
      display: inline-block;
    }
  }
}

@keyframes rotating {
  0% {
    transform: rotate(0deg)
  }

  100% {
    transform: rotate(1turn)
  }
}
</style>